﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using VCDIFF.Commands;
using VCDIFF.Create;
using VCDIFF.Rebuild;
using VCDIFF.Differencing.DiffAlgorithm;
using System.IO;

namespace VCDIFF.Differencing.DiffAlgorithm
{
    public class ExecuteDiff
    {
        public static void Execute(string filePath, DiffList_TextFile source, 
            DiffList_TextFile destination, ArrayList differences)
        {
            IList<Command> commandsList = new List<Command>();

            byte[] sourceBytes = File.ReadAllBytes(filePath);

            int i;
            UInt64 index = 0;

            foreach (DiffResultSpan drs in differences)
            {
                switch (drs.Status)
                {
                    case DiffResultSpanStatus.DeleteSource: break;
                    case DiffResultSpanStatus.NoChange:
                        for (i = 0; i < drs.Length; i++)
                        {
                            UInt64 size = Convert.ToUInt64(((TextLine)source.GetByIndex(drs.SourceIndex + i)).Line.Length);
                            SourceFile sourceFile = new SourceFile(ToByteArray(((TextLine)source.GetByIndex(drs.SourceIndex + i)).Line));
                            
                            commandsList.Add(new Copy(size, index, sourceFile));
                            commandsList.Add(new Run(1, Convert.ToByte('\n')));

                            index += size + 1;
                        }

                        break;
                    case DiffResultSpanStatus.AddDestination:
                        for (i = 0; i < drs.Length; i++)
                        {
                            byte[] bytesTable = ToByteArray(((TextLine)destination.GetByIndex(drs.DestIndex + i)).Line);
                            UInt64 size = Convert.ToUInt64(bytesTable.LongLength);

                            commandsList.Add(new Add(size, bytesTable));
                            commandsList.Add(new Run(1, Convert.ToByte('\n')));

                            index += size;                            
                        }

                        break;
                    case DiffResultSpanStatus.Replace:
                        for (i = 0; i < drs.Length; i++)
                        {
                            byte[] bytesTable = ToByteArray(((TextLine)destination.GetByIndex(drs.DestIndex + i)).Line);
                            UInt64 size = Convert.ToUInt64(bytesTable.LongLength);

                            commandsList.Add(new Add(size, bytesTable));
                            commandsList.Add(new Run(1, Convert.ToByte('\n')));

                            index += size;
                        }

                        break;
                }
            }

            WriteVCDIFF(commandsList, sourceBytes.LongLength, filePath);
        }

        private static void WriteVCDIFF(IList<Command> commandsList, Int64 sourceLength, string filePath)
        {
            CreateHeader header = new CreateHeader();
            CreateWindow window = new CreateWindow(sourceLength, commandsList);

            File.WriteAllBytes(filePath + "_vcdiff", MergeArray(header.GetHeaderData(), window.GetWindowData()));
        }

        private static byte[] MergeArray(byte[] firstArray, byte[] secondArray)
        {
            byte[] array = new byte[firstArray.Length + secondArray.Length];
            int index = 0;

            for (index = 0; index < firstArray.Length; index++)
                array[index] = firstArray[index];

            for (int i = 0; i < secondArray.Length; i++)
            {
                array[index] = secondArray[i];
                index++;
            }

            return array;
        }

        private static byte[] ToByteArray(IList<Command> commandsList)
        {
            IList<byte> byteList = new List<Byte>();

            foreach (Command command in commandsList)
                foreach (byte b in command.ToByteArray())
                    byteList.Add(b);

            byte[] byteArray = new byte[byteList.Count];

            for (int i = 0; i < byteArray.Length; i++)
                byteArray[i] = byteList[i];

            return byteArray;
        }

        private static byte[] ToByteArray(string s)
        {
            byte[] byteArray = new byte[s.Length];

            for (int i = 0; i < s.Length; i++)
                byteArray[i] = Convert.ToByte(s.ToCharArray()[i]);

            return byteArray;
        }
    }
}
